The symmetry-exceptional slopes of K=t09847 are (-3,1), (-2,1), and (2,1). All three have symmetry group Z/2+Z/2 and thus they can be written in three different ways as DBC. Here we determine all these three ways. Since K is strongly invertible all slopes can be written in one way as DBC branched along a link L in S3. That means for the three symmetry-exceptional fillings we need to find another 2 ways to write them as DBCs.
import time
import snappy
import pandas
DBChomologies = pandas.read_csv("DBChomologies_non_alternating.csv") # We load a list of all non alternating links in
#the HTLinkExteriors together with their homologies (if the homology is cyclic) and their crossing numbers.
### REMARK: To build the list we need to run the code in the file Build_DBChomologies which takes around 60 minutes.
#Here we just load it.
def double_branched_cover(link):
"""
Returns the double branched cover of the link.
"""
L=link.copy()
for i in range(L.num_cusps()):
L.dehn_fill((2,0),i)
for cov in L.covers(2):
if (2.0, 0.0) not in cov.cusp_info('filling'):
return cov
def better_is_isometric_to(X,Y,index):
"""
Returns True if X and Y are isometric.
Returns False if X and Y have different homologies. TO DO: Use volume to rigorously distinguish X and Y.
Returns 'unclear' if SnapPy cannot verify it.
The higher the index the harder SnapPy tries.
"""
w='unclear'
if X.homology()!=Y.homology():
w=False
if w=='unclear':
for i in (0,index):
try:
w=X.is_isometric_to(Y)
except RuntimeError:
pass
except snappy.SnapPeaFatalError:
pass
if w==True:
break
if w==False:
w='unclear'
X.randomize()
Y.randomize()
i=i+1
return w
def possible_DBC(homologies,max_crossings=15):
"""
Takes a list of orders of homologies and returns a list consisting of all DBC of alternating links in the HT link table with that homologies together with the link names.
"""
DBCList=[]
LINKS=[]
for order in homologies:
LINKS=LINKS+DBChomologies.loc[(DBChomologies['homology']==order) & (DBChomologies['crossings']<=max_crossings)]['knot'].tolist()
for link in LINKS:
L=snappy.Manifold(link)
D=double_branched_cover(L)
DBCList.append([D,link])
return DBCList
### The following two functions are written by Dunfield and search for positive triangulations.
def all_positive(manifold):
return manifold.solution_type() == 'all tetrahedra positively oriented'
def find_positive_triangulation(manifold, tries=100):
M = manifold.copy()
for i in range(tries):
if all_positive(M):
return M
M.randomize()
for d in M.dual_curves():
X = M.drill(d)
X = X.filled_triangulation()
X.dehn_fill((1,0))
for i in range(tries):
if all_positive(X):
return X
X.randomize()
# In the closed case, here is another trick.
if all(not c for c in M.cusp_info('is_complete')):
for i in range(tries):
# Drills out a random edge
X = M.__class__(M.filled_triangulation())
if all_positive(X):
return X
M.randomize()
def better_find_positive_triangulation(M,tries=1):
'''
Search for a positive triangulation, but ignores errors.
'''
RandomizeCount=0
while RandomizeCount<tries:
try:
X=find_positive_triangulation(M)
return X
except snappy.SnapPeaFatalError:
M.randomize()
RandomizeCount=RandomizeCount+1
return None
def is_alternating(knot,slope,try_hard=False,index=10,tries=1,max_cro=15):
'''
Checks if the slope is alternating.
'''
K=snappy.Manifold(knot)
K.dehn_fill(slope)
DBC=possible_DBC([K.homology().order()],max_crossings=max_cro)
for D in DBC:
w=better_is_isometric_to(D[0],K,index)
if w==True:
return [slope,D[1]]
if try_hard:
X=better_find_positive_triangulation(K,tries)
if X is not None:
for D in DBC:
Y=better_find_positive_triangulation(D[0],tries)
if Y is not None:
w=better_is_isometric_to(X,Y,index)
if w==True:
return [slope,D[1]]
return False
--------------------------------------------------------------------------- ModuleNotFoundError Traceback (most recent call last) /var/folders/x0/69y_4spx515cn735q1qy2ylm0000gn/T/ipykernel_60620/2895650112.py in <module> 1 import time 2 import snappy ----> 3 import pandas 4 DBChomologies = pandas.read_csv("DBChomologies_non_alternating.csv") # We load a list of all non alternating links in 5 #the HTLinkExteriors together with their homologies (if the homology is cyclic) and their crossing numbers. ModuleNotFoundError: No module named 'pandas'
K=snappy.Manifold('t09847')
exc_sym_slopes=[(-3,1),(-2,1),(2,1)]
for s in exc_sym_slopes:
K.dehn_fill(s)
print(s,K.symmetry_group())
(-3, 1) Z/2 + Z/2 (-2, 1) Z/2 + Z/2 (2, 1) Z/2 + Z/2
start_time = time.time()
branching_sets=[]
knot='t09847'
for slope in exc_sym_slopes:
w=is_alternating(knot,slope,index=25,max_cro=15)
if w!=False:
branching_sets.append([knot,w[0],w[1]])
print('We found a branching set:',knot,slope,w[1])
print('Total number of branching sets we have found:', len(branching_sets))
print('Total time taken: %s minutes ---' % ((time.time() - start_time)/60))
We found a branching set: t09847 (-3, 1) K15n88871 We found a branching set: t09847 (-2, 1) L14n24290 We found a branching set: t09847 (2, 1) L14n31171 Total number of branching sets we have found: 3 Total time taken: 0.5895553231239319 minutes ---
Next we will search for branching sets in more general manifolds. For that we will take links in the HT link tables fill some one component of it to get a surgery diagram of a link in a manifold. Then we take the double branched cover of that link and search for a match with a symmetric filling on A.
DBChomologies_branching = pandas.read_csv("DBChomologies_one_filling.csv")
### REMARK: To build the list we need to run the code in the file Build_DBChomologies which takes around 3 days. Here we just load it.
def double_branched_cover(link):
"""
Returns the double branched covers of the link. This works also for links in a more general manifold.
Note that a knot in a more general manifold may have more than one double branched cover
(or no double branched cover at all if the knot represents a primitive element in homology).
This function will return the complete list of all double branched covers of the link.
"""
L=link.copy()
for i in range(L.num_cusps()):
if L.cusp_info(i).filling==(0.0, 0.0):
L.dehn_fill((2,0),i)
return [cov for cov in L.covers(2) if (2.0, 0.0) not in cov.cusp_info('filling')]
def possible_DBC_surgery_diagrams(homologies,max_crossings=15):
"""
Reads off the possible surgery diagrams.
"""
DBCList=[]
LINKS=[]
CUSPS=[]
SLOPES_STRINGS=[]
for order in homologies:
LINKS=LINKS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['knot'].tolist()
CUSPS=CUSPS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['cusp'].tolist()
SLOPES_STRINGS=SLOPES_STRINGS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['filling'].tolist()
SLOPES=[]
for string in SLOPES_STRINGS:
string_without_brackets=string[1:-1]
SLOPES.append(tuple(map(int, string_without_brackets.split(', '))))
for i in range(0,len(LINKS)):
DBCList.append([LINKS[i],SLOPES[i],CUSPS[i]])
return DBCList
def search_for_branching_set(knot,slope,try_hard=False,index=10,tries=1,max_crossings=15):
'''
Searchs for a surgery diagram of the rbanching set
'''
K=snappy.Manifold(knot)
K.dehn_fill(slope)
DBC=possible_DBC_surgery_diagrams([K.homology().order()],max_crossings)
for DIAGRAM in DBC:
L=snappy.Manifold(DIAGRAM[0])
L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
for D in double_branched_cover(L):
w=better_is_isometric_to(D,K,index)
if w==True:
return [[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
if try_hard:
X=better_find_positive_triangulation(K,tries)
if X is not None:
for DIAGRAM in DBC:
L=snappy.Manifold(DIAGRAM[0])
L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
for D in double_branched_cover(L):
Y=better_find_positive_triangulation(D,tries)
if Y is not None:
w=better_is_isometric_to(X,Y,index)
if w==True:
return [[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
return False
def search_for_three_branching_sets(knot,slope,try_hard=False,index=10,tries=1,max_crossings=15):
'''
Searchs for a surgery diagram of the branching set
'''
BRANCHING_SETS=[]
K=snappy.Manifold(knot)
K.dehn_fill(slope)
homologies=[]
DBC=possible_DBC_surgery_diagrams([K.homology().order()],max_crossings)
for DIAGRAM in DBC:
L=snappy.Manifold(DIAGRAM[0])
L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
for D in double_branched_cover(L):
w=better_is_isometric_to(D,K,index)
if w==True:
BRANCHING_SETS=BRANCHING_SETS+[[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
for i in range(L.num_cusps()):
if L.cusp_info(i).filling==(0.0, 0.0):
L.dehn_fill((1,0),i)
if L.homology().order() not in homologies:
homologies.append(L.homology().order())
if len(homologies)==3:
return BRANCHING_SETS
if try_hard:
X=better_find_positive_triangulation(K,tries)
if X is not None:
for DIAGRAM in DBC:
L=snappy.Manifold(DIAGRAM[0])
L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
for D in double_branched_cover(L):
Y=better_find_positive_triangulation(D,tries)
if Y is not None:
w=better_is_isometric_to(X,Y,index)
if w==True:
BRANCHING_SETS=BRANCHING_SETS+[[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
for i in range(L.num_cusps()):
if L.cusp_info(i).filling==(0.0, 0.0):
L.dehn_fill((1,0),i)
if L.homology().order() not in homologies:
homologies.append(L.homology().order())
if len(homologies)==3:
return BRANCHING_SETS
return BRANCHING_SETS
start_time = time.time()
knot='t09847'
for slope in exc_sym_slopes:
w=search_for_three_branching_sets(knot,slope,index=3,max_crossings=15)
if w==[]:
w=False
if w!=False:
branching_sets=branching_sets+w
print('We found a branching set:',w)
print('Total number of branching sets we have found:', len(branching_sets))
print('Total time taken: %s minutes ---' % ((time.time() - start_time)/60))
We found a branching set: [['t09847', (-3, 1), 'L13n5917', (-3, 1), 0], ['t09847', (-3, 1), 'L14n24778', (-3, 1), 0]] We found a branching set: [['t09847', (-2, 1), 'L11n420', (5, 3), 0], ['t09847', (-2, 1), 'L13n7290', (-2, 1), 1], ['t09847', (-2, 1), 'L13n9355', (-5, 2), 1], ['t09847', (-2, 1), 'L13n9861', (2, 1), 1]] We found a branching set: [['t09847', (2, 1), 'L12n1082', (-2, 1), 0]] Total number of branching sets we have found: 10 Total time taken: 112.62691159645716 minutes ---
branching_sets
[['t09847', (-3, 1), 'K15n88871'], ['t09847', (-2, 1), 'L14n24290'], ['t09847', (2, 1), 'L14n31171'], ['t09847', (-3, 1), 'L13n5917', (-3, 1), 0], ['t09847', (-3, 1), 'L14n24778', (-3, 1), 0], ['t09847', (-2, 1), 'L11n420', (5, 3), 0], ['t09847', (-2, 1), 'L13n7290', (-2, 1), 1], ['t09847', (-2, 1), 'L13n9355', (-5, 2), 1], ['t09847', (-2, 1), 'L13n9861', (2, 1), 1], ['t09847', (2, 1), 'L12n1082', (-2, 1), 0]]
Since the knot K is strongly invertible we have always at least on way to write the knot as a DBC over a link in S3. The first 3 branching sets in S3 are these generic branching sets. The other surgery descriptions yield surgery descriptions in lens spaces. We still need to find one more branching set for (-3,1) and one more for (2,1). For that we search for surgery descriptions where we fill two surgery curves.
K16=snappy.Manifold('t09847(2,1)')
K16
t09847(2,1)
K16.homology()
Z/16
K21=snappy.Manifold('t09847(-3,1)')
K21
t09847(-3,1)
K21.homology()
Z/21
def better_is_isometric_to(X,Y,index=10,return_isometries=False):
"""
Returns True if X and Y are isometric.
Returns False if it is unclear.
"""
w=False
for i in (0,index):
try:
w=X.is_isometric_to(Y,return_isometries)
return w
except (RuntimeError, snappy.SnapPeaFatalError):
X.randomize()
Y.randomize()
i=i+1
return w
def gcd(a, b):
while b != 0:
a, b = b, a % b
return a
def coprime(a, b):
return gcd(a, b) == 1
slope_set=[]
for q in range (1,5):
for p in range (2,11):
if coprime(p,q)==True:
slope_set.append((p,q))
slope_set.append((-p,q))
def double_branched_cover(link):
"""
Returns the double branched covers of the link. This works also for links in a more general manifold.
Note that a knot in a more general manifold may have more than one double branched cover
(or no double branched cover at all if the knot represents a primitive element in homology).
This function will return the complete list of all double branched covers of the link.
"""
L=link.copy()
for i in range(L.num_cusps()):
if L.cusp_info(i).filling==(0.0, 0.0):
L.dehn_fill((2,0),i)
return [cov for cov in L.covers(2) if (2.0, 0.0) not in cov.cusp_info('filling')]
start_time = time.time()
for L in snappy.HTLinkExteriors(knots_vs_links='links'):
if L.num_cusps()==3:
for r in slope_set:
for s in slope_set:
L.dehn_fill([r,s,(0,0)])
DBC=double_branched_cover(L)
for D in DBC:
hom=D.homology()
if len(hom)==1:
if D.homology().coefficients==[16]:
if better_is_isometric_to(D,K16):
print(L)
if D.homology().coefficients==[21]:
if better_is_isometric_to(D,K21):
print(L)
L.dehn_fill([r,(0,0),s])
DBC=double_branched_cover(L)
for D in DBC:
hom=D.homology()
if len(hom)==1:
if D.homology().coefficients==[16]:
if better_is_isometric_to(D,K16):
print(L)
if D.homology().coefficients==[21]:
if better_is_isometric_to(D,K21):
print(L)
L.dehn_fill([(0,0),r,s])
DBC=double_branched_cover(L)
for D in DBC:
hom=D.homology()
if len(hom)==1:
if D.homology().coefficients==[16]:
if better_is_isometric_to(D,K16):
print(L)
if D.homology().coefficients==[21]:
if better_is_isometric_to(D,K21):
print(L)
print("--- Time taken: %s hours ---" % (time.time() - start_time)/3600)
L10n92(0,0)(-7,4)(-8,3) L11n294(-2,3)(-5,1)(0,0) L11n420(0,0)(7,3)(4,3)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) /var/folders/x0/69y_4spx515cn735q1qy2ylm0000gn/T/ipykernel_58315/2258601642.py in <module> 16 if D.homology().coefficients==[Integer(21)]: 17 if better_is_isometric_to(D,K21): ---> 18 print(L) 19 L.dehn_fill([r,(Integer(0),Integer(0)),s]) 20 DBC=double_branched_cover(L) cython/core/triangulation.pyx in SnapPy.Triangulation.__repr__() cython/core/manifold.pyx in SnapPy.Manifold.cusp_info() ~/.sage/local/lib/python3.10/site-packages/snappy/__init__.py in <lambda>(n) 121 from .sage_helper import _within_sage 122 if _within_sage: --> 123 to_sage = lambda n : n.sage() 124 Manifold.use_field_conversion(to_sage) 125 ManifoldHP.use_field_conversion(to_sage) ~/.sage/local/lib/python3.10/site-packages/snappy/number.py in sage(self) 534 return RealField(self._precision)(self.gen) 535 elif self.gen.type() == 't_COMPLEX': --> 536 return ComplexField(self._precision)(self.gen) 537 else: 538 return self.gen.sage() /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/rings/complex_mpfr.pyx in sage.rings.complex_mpfr.ComplexField_class.__call__ (build/cythonized/sage/rings/complex_mpfr.c:7336)() 482 if im is not None: 483 x = x, im --> 484 return Parent.__call__(self, x) 485 486 def _element_constructor_(self, x): /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/structure/parent.pyx in sage.structure.parent.Parent.__call__ (build/cythonized/sage/structure/parent.c:9450)() 895 if mor is not None: 896 if no_extra_args: --> 897 return mor._call_(x) 898 else: 899 return mor._call_with_args(x, args, kwds) /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/structure/coerce_maps.pyx in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (build/cythonized/sage/structure/coerce_maps.c:4734)() 159 print(type(C), C) 160 print(type(C._element_constructor), C._element_constructor) --> 161 raise 162 163 cpdef Element _call_with_args(self, x, args=(), kwds={}): /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/structure/coerce_maps.pyx in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (build/cythonized/sage/structure/coerce_maps.c:4626)() 154 cdef Parent C = self._codomain 155 try: --> 156 return C._element_constructor(x) 157 except Exception: 158 if print_warnings: /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/rings/complex_mpfr.pyx in sage.rings.complex_mpfr.ComplexField_class._element_constructor_ (build/cythonized/sage/rings/complex_mpfr.c:8251)() 533 534 try: --> 535 return self(x.sage()) 536 except (AttributeError, TypeError): 537 pass cypari2/gen.pyx in cypari2.gen.Gen.sage() /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/libs/pari/convert_sage.pyx in sage.libs.pari.convert_sage.gen_to_sage (build/cythonized/sage/libs/pari/convert_sage.c:4800)() 39 40 ---> 41 cpdef gen_to_sage(Gen z, locals=None): 42 """ 43 Convert a PARI gen to a Sage/Python object. /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/libs/pari/convert_sage.pyx in sage.libs.pari.convert_sage.gen_to_sage (build/cythonized/sage/libs/pari/convert_sage.c:4034)() 296 else: 297 K = QuadraticField(-1, 'i') --> 298 return K([gen_to_sage(real), gen_to_sage(imag)]) 299 elif t == t_VEC or t == t_COL: 300 return [gen_to_sage(x, locals) for x in z.python_list()] /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/libs/pari/convert_sage.pyx in sage.libs.pari.convert_sage.gen_to_sage (build/cythonized/sage/libs/pari/convert_sage.c:4666)() 324 from sage.misc.sage_eval import sage_eval 325 locals = {} if locals is None else locals --> 326 return sage_eval(str(z), locals=locals) 327 328 /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/misc/sage_eval.py in sage_eval(source, locals, cmds, preparse) 196 return locals['_sage_eval_returnval_'] 197 else: --> 198 return eval(source, sage.all.__dict__, locals) 199 200 /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/all.py in <module> NameError: name 'nan' is not defined
The first two surgery descriptions yield the two different manifold. And we can check via KLO that the yield links in different lens spaces.
B21=snappy.Manifold('L11n294(-2,3)(-5,1)(0,0)')
D=double_branched_cover(B21)[0]
D.homology()
Z/21
D.is_isometric_to(K21)
True
B16=snappy.Manifold('L10n92(0,0)(-7,4)(-8,3)')
D=double_branched_cover(B16)[0]
D.homology()
Z/16
D.is_isometric_to(K16)
True